import template from './sw-media-upload-v2.html.twig';
import './sw-media-upload-v2.scss';

const { Mixin, Context } = Shopware;
const { fileReader } = Shopware.Utils;
const { fileSize } = Shopware.Utils.format;
const INPUT_TYPE_FILE_UPLOAD = 'file-upload';
const INPUT_TYPE_URL_UPLOAD = 'url-upload';

/**
 * @status ready
 * @description The <u>sw-media-upload-v2</u> component is used wherever an upload is needed. It supports drag & drop-,
 * file- and url-upload and comes in various forms.
 * @sw-package discovery
 * @example-type code-only
 * @component-example
 * <sw-media-upload-v2
 *     upload-tag="my-upload-tag"
 *     variant="regular"
 *     :allow-multi-select="false"
 *     label="My image-upload"
 * ></sw-media-upload-v2>
 */
// eslint-disable-next-line sw-deprecation-rules/private-feature-declarations
export default {
    template,

    inject: [
        'repositoryFactory',
        'mediaService',
        'feature',
        'fileValidationService',
    ],

    emits: [
        'media-drop',
        'media-upload-sidebar-open',
        'media-upload-remove-image',
        'media-upload-add-file',
    ],

    mixins: [
        Mixin.getByName('notification'),
    ],

    props: {
        source: {
            type: [
                Object,
                String,
                File,
            ],
            required: false,
            default: null,
        },

        variant: {
            type: String,
            required: false,
            validValues: [
                'compact',
                'regular',
                'small',
            ],
            validator(value) {
                return [
                    'compact',
                    'regular',
                    'small',
                ].includes(value);
            },
            default: 'regular',
        },

        uploadTag: {
            type: String,
            required: true,
        },

        allowMultiSelect: {
            type: Boolean,
            required: false,
            // eslint-disable-next-line vue/no-boolean-default
            default: true,
        },

        addFilesOnMultiselect: {
            type: Boolean,
            required: false,
            default: false,
        },

        // eslint-disable-next-line vue/require-default-prop
        label: {
            type: String,
            required: false,
            default: null,
        },

        buttonLabel: {
            type: String,
            required: false,
            default: '',
        },

        defaultFolder: {
            type: String,
            required: false,
            validator(value) {
                return value.length > 0;
            },
            default: null,
        },

        targetFolderId: {
            type: String,
            required: false,
            default: null,
        },

        helpText: {
            type: String,
            required: false,
            default: null,
        },

        sourceContext: {
            type: Object,
            required: false,
            default: null,
        },

        fileAccept: {
            type: String,
            required: false,
            default: '*/*',
        },

        extensionAccept: {
            type: String,
            required: false,
            default: null,
        },

        maxFileSize: {
            type: Number,
            required: false,
            default: null,
        },

        disabled: {
            type: Boolean,
            required: false,
            default: false,
        },

        privateFilesystem: {
            type: Boolean,
            required: false,
            default: false,
        },

        useFileData: {
            type: Boolean,
            required: false,
            default: false,
        },

        required: {
            type: Boolean,
            required: false,
            default: false,
        },

        onMediaUploadSidebarOpen: {
            type: Function,
            required: false,
            default: null,
        },
    },

    data() {
        return {
            multiSelect: this.allowMultiSelect,
            inputType: INPUT_TYPE_FILE_UPLOAD,
            preview: null,
            isDragActive: false,
            defaultFolderId: null,
            isUploadUrlFeatureEnabled: Shopware.Store.get('context').app.config?.settings?.enableUrlFeature ?? false,
            isLoading: false,
        };
    },

    computed: {
        defaultFolderRepository() {
            return this.repositoryFactory.create('media_default_folder');
        },

        mediaRepository() {
            return this.repositoryFactory.create('media', '', {
                keepApiErrors: true,
            });
        },

        showPreview() {
            return !this.multiSelect;
        },

        hasOpenMediaButtonListener() {
            return !!this.onMediaUploadSidebarOpen;
        },

        isDragActiveClass() {
            return {
                'is--active': this.isDragActive,
                'is--multi': this.variant === 'regular' && !!this.multiSelect,
                'is--small': this.variant === 'small',
            };
        },

        mediaFolderId() {
            return this.defaultFolderId || this.targetFolderId;
        },

        isUrlUpload() {
            return this.inputType === INPUT_TYPE_URL_UPLOAD;
        },

        isFileUpload() {
            return this.inputType === INPUT_TYPE_FILE_UPLOAD;
        },

        uploadUrlFeatureEnabled() {
            return this.isUploadUrlFeatureEnabled;
        },

        swFieldLabelClasses() {
            return {
                'is--required': this.required,
            };
        },

        buttonFileUploadLabel() {
            if (this.buttonLabel === '') {
                return this.$tc('global.sw-media-upload-v2.buttonFileUpload');
            }

            return this.buttonLabel;
        },

        mediaNameFilter() {
            return Shopware.Filter.getByName('mediaName');
        },
    },

    watch: {
        async defaultFolder() {
            this.defaultFolderId = await this.getDefaultFolderId();
        },

        disabled(newValue) {
            if (newValue) {
                this.isDragActive = false;
            }
        },
    },

    created() {
        this.createdComponent();
    },

    mounted() {
        this.mountedComponent();
    },

    beforeUnmount() {
        this.beforeDestroyComponent();
    },

    methods: {
        async createdComponent() {
            this.mediaService.addListener(this.uploadTag, this.handleMediaServiceUploadEvent);
            if (this.mediaFolderId) {
                return;
            }

            if (this.defaultFolder) {
                this.isLoading = true;
                this.defaultFolderId = await this.getDefaultFolderId();
                this.isLoading = false;
            }
        },

        mountedComponent() {
            if (this.$refs.dropzone) {
                [
                    'dragover',
                    'drop',
                ].forEach((event) => {
                    window.addEventListener(event, this.stopEventPropagation, false);
                });
                this.$refs.dropzone.addEventListener('drop', this.onDrop);

                window.addEventListener('dragenter', this.onDragEnter);
                window.addEventListener('dragleave', this.onDragLeave);
            }
        },

        beforeDestroyComponent() {
            this.mediaService.removeByTag(this.uploadTag);
            this.mediaService.removeListener(this.uploadTag, this.handleMediaServiceUploadEvent);

            [
                'dragover',
                'drop',
            ].forEach((event) => {
                window.removeEventListener(event, this.stopEventPropagation, false);
            });
            if (this.$refs.dropzone) {
                this.$refs.dropzone.removeEventListener('drop', this.onDrop);
            }

            window.removeEventListener('dragenter', this.onDragEnter);
            window.removeEventListener('dragleave', this.onDragLeave);
        },

        /*
         * Drop Handler
         */
        onDrop(event) {
            if (this.disabled) {
                return;
            }

            const newMediaFiles = Array.from(event.dataTransfer.files);
            this.isDragActive = false;

            if (newMediaFiles.length === 0) {
                return;
            }

            this.handleFileCheck(newMediaFiles);
        },

        onDropMedia(dragData) {
            if (this.disabled) {
                return;
            }

            this.$emit('media-drop', dragData.mediaItem);
        },

        onDragEnter() {
            if (this.disabled) {
                return;
            }

            this.isDragActive = true;
        },

        onDragLeave(event) {
            if (event.screenX === 0 && event.screenY === 0) {
                this.isDragActive = false;
                return;
            }

            const target = event.target;

            if (target.closest('.sw-media-upload-v2__dropzone')) {
                return;
            }

            this.isDragActive = false;
        },

        stopEventPropagation(event) {
            event.preventDefault();
            event.stopPropagation();
        },

        /*
         * Click handler
         */
        onClickUpload() {
            this.$refs.fileInput.click();
        },

        useUrlUpload() {
            this.inputType = INPUT_TYPE_URL_UPLOAD;
        },

        useFileUpload() {
            this.inputType = INPUT_TYPE_FILE_UPLOAD;
        },

        onClickOpenMediaSidebar() {
            this.$emit('media-upload-sidebar-open');
        },

        onRemoveMediaItem() {
            if (this.disabled) {
                return;
            }

            this.preview = null;
            this.$emit('media-upload-remove-image');
        },

        /*
         * entry points
         */
        async onUrlUpload({ url, fileExtension }) {
            if (!this.multiSelect) {
                this.mediaService.removeByTag(this.uploadTag);
                this.preview = url;
            }

            let fileInfo;

            try {
                fileInfo = fileReader.getNameAndExtensionFromUrl(url);
            } catch (error) {
                this.createNotificationError({
                    title: this.$tc('global.default.error'),
                    message: this.$tc('global.sw-media-upload-v2.notification.invalidUrl.message'),
                });

                return;
            }

            if (fileExtension) {
                fileInfo.extension = fileExtension;
            }

            const targetEntity = this.getMediaEntityForUpload();

            await this.mediaRepository.save(targetEntity, Context.api);
            this.mediaService.addUpload(this.uploadTag, {
                src: url,
                targetId: targetEntity.id,
                isPrivate: targetEntity.private,
                ...fileInfo,
            });

            this.useFileUpload();
        },

        onFileInputChange() {
            const newMediaFiles = Array.from(this.$refs.fileInput.files);

            if (!newMediaFiles.length) {
                return;
            }

            this.handleFileCheck(newMediaFiles);

            this.$refs.fileForm.reset();
        },

        /*
         * Helper functions
         */
        async handleUpload(newMediaFiles) {
            if (!this.multiSelect) {
                this.mediaService.removeByTag(this.uploadTag);
                newMediaFiles = [newMediaFiles.pop()];
                this.preview = newMediaFiles[0];
            } else {
                if (!this.preview) {
                    this.preview = [];
                }

                if (this.addFilesOnMultiselect) {
                    this.preview = [
                        ...this.preview,
                        ...newMediaFiles,
                    ];
                } else {
                    this.preview = newMediaFiles;
                }
            }

            const syncEntities = [];

            const uploadData = newMediaFiles.map((fileHandle) => {
                const { fileName, extension } = fileReader.getNameAndExtensionFromFile(fileHandle);
                const targetEntity = this.getMediaEntityForUpload();
                syncEntities.push(targetEntity);

                return {
                    src: fileHandle,
                    targetId: targetEntity.id,
                    fileName,
                    extension,
                    isPrivate: targetEntity.private,
                };
            });

            await this.mediaRepository.saveAll(syncEntities, Context.api);
            await this.mediaService.addUploads(this.uploadTag, uploadData);
        },

        getMediaEntityForUpload() {
            const mediaItem = this.mediaRepository.create();
            mediaItem.mediaFolderId = this.mediaFolderId;
            mediaItem.private = this.privateFilesystem;

            return mediaItem;
        },

        async getDefaultFolderId() {
            return this.mediaService.getDefaultFolderId(this.defaultFolder);
        },

        handleMediaServiceUploadEvent({ action }) {
            if (action === 'media-upload-fail') {
                this.onRemoveMediaItem();
            }
        },

        checkFileSize(file) {
            if (this.maxFileSize === null || file.size <= this.maxFileSize || file.fileSize <= this.maxFileSize) {
                return true;
            }

            this.createNotificationError({
                message: this.$tc(
                    'global.sw-media-upload-v2.notification.invalidFileSize.message',
                    {
                        name: file.name || file.fileName,
                        limit: fileSize(this.maxFileSize),
                    },
                    0,
                ),
            });
            return false;
        },

        checkFileType(file) {
            // Set file type and file name if file is a media entity item
            if (!file?.type && file.id) {
                file.type = file.mimeType;
            }

            if (!file?.name && file.id) {
                file.name = file.fileName;
            }

            const isValidFile = () => {
                if (this.extensionAccept) {
                    return this.fileValidationService.checkByExtension(file, this.extensionAccept);
                }

                if (this.fileAccept) {
                    return this.fileValidationService.checkByType(file, this.fileAccept);
                }

                return false;
            };

            if (isValidFile()) {
                return true;
            }

            this.createNotificationError({
                message: this.$tc(
                    'global.sw-media-upload-v2.notification.invalidFileType.message',
                    {
                        name: file.name,
                        supportedTypes: this.extensionAccept || this.fileAccept,
                    },
                    0,
                ),
            });

            return false;
        },

        handleFileCheck(files) {
            const checkedFiles = files.filter((file) => {
                return this.checkFileSize(file) && this.checkFileType(file);
            });

            if (this.useFileData) {
                this.preview = !this.multiSelect ? checkedFiles[0] : null;
                this.$emit('media-upload-add-file', checkedFiles);
            } else {
                this.handleUpload(checkedFiles);
            }
        },
    },
};
